package com.lj.queue.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.lj.common.exception.CommonException;
import com.lj.common.utils.excel.ExcelSheet;
import com.lj.common.utils.excel.ExcelUtil;
import com.lj.mp.standard.StandardServiceImpl;
import com.lj.mp.utils.PageQueryUtils;
import com.lj.queue.entity.DelayQueueMessage;
import com.lj.queue.entity.DelayQueueMeta;
import com.lj.queue.enums.DelayQueueMessageStatus;
import com.lj.queue.mapper.DelayQueueMessageMapper;
import com.lj.queue.param.DelayQueueMessageImportParam;
import com.lj.queue.param.DelayQueueMessagePageParam;
import com.lj.queue.param.DelayQueueMessageSaveParam;
import com.lj.queue.param.DelayQueueMessageUpdateParam;
import com.lj.queue.result.DelayQueueMessageExportResult;
import com.lj.queue.result.DelayQueueMessageInfoResult;
import com.lj.queue.result.DelayQueueMessagePageResult;
import com.lj.queue.service.DelayQueueMessageService;
import com.lj.queue.service.DelayQueueMetaService;
import com.lj.queue.utils.DelayQueueUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 延迟队列消息表 服务实现类
 * </p>
 *
 * @author lj
 * @since 2025-07-21 16:55:02
 */
@Service
public class DelayQueueMessageServiceImpl extends StandardServiceImpl<DelayQueueMessageMapper, DelayQueueMessage> implements DelayQueueMessageService {

    @Resource
    private DelayQueueMetaService queueMetaService;

    @Override
    public IPage<DelayQueueMessagePageResult> page(DelayQueueMessagePageParam param) {
        return this.page(PageQueryUtils.getPage(param), getQueryWrapper(param)).convert(DelayQueueMessagePageResult::of);
    }

    private LambdaQueryWrapper<DelayQueueMessage> getQueryWrapper(DelayQueueMessagePageParam param) {
        return lambdaQueryWrapper()
                .eq(DelayQueueMessage::getQueueId, param.getQueueId())
                .eq(ObjectUtil.isNotEmpty(param.getStatus()), DelayQueueMessage::getStatus, param.getStatus())
                .between(ObjectUtil.isNotEmpty(param.getExpectTimeStart()) && ObjectUtil.isNotEmpty(param.getExpectTimeEnd()),
                        DelayQueueMessage::getExpectTime,
                        param.getExpectTimeStart(), param.getExpectTimeEnd())
                .orderByDesc(CollUtil.isEmpty(param.getSortList()), DelayQueueMessage::getExpectTime);
    }

    @Override
    public DelayQueueMessageInfoResult info(Long id) {
        return DelayQueueMessageInfoResult.of(this.getById(id));
    }

    @Override
    public void save(DelayQueueMessageSaveParam param) {
        DelayQueueMeta queueMeta = queueMetaService.getById(param.getQueueId());
        if (queueMeta == null) {
            throw new CommonException("队列不存在!");
        }
        DelayQueueUtils.pushMessage(queueMeta, param.toEntity());
    }

    @Override
    public boolean pushMessage(DelayQueueMeta queueMeta, List<DelayQueueMessage> messageList) {
        if (queueMeta == null || CollUtil.isEmpty(messageList)) {
            return false;
        }
        // 如果消息在下次预加载之前触发直接加到队列中(可能预加载无法加载到了)
        LocalDateTime nextPreLoadTime = queueMeta.getLastLoadTime().plus(queueMeta.getPreloadDistance(), queueMeta.getTimeUnit().getChronoUnit());
        for (DelayQueueMessage queueMessage : messageList) {
            queueMessage.setQueueId(queueMeta.getId());
            if (queueMessage.getExpectTime().isBefore(nextPreLoadTime)) {
                queueMessage.setStatus(DelayQueueMessageStatus.loaded);
            } else {
                queueMessage.setStatus(DelayQueueMessageStatus.pending);
            }
        }
        // 这里要先入库再推消息到队列中，防止消息立马执行时数据库中没有数据导致报错
        this.insertBatchSomeColumn(messageList);
        List<DelayQueueMessage> pushList = messageList.stream().filter(qm -> DelayQueueMessageStatus.loaded.equals(qm.getStatus())).toList();
        DelayQueueUtils.pushQueue(queueMeta.getName(), pushList);
        return true;
    }

    @Override
    public void update(DelayQueueMessageUpdateParam param) {
        // 判断是否可修改
        DelayQueueMessage queueMessage = this.getById(param.getId());
        if (queueMessage == null) {
            throw new CommonException("消息不存在!");
        }
        if (!DelayQueueMessageStatus.pending.equals(queueMessage.getStatus())) {
            throw new CommonException("消息状态无法修改!");
        }
        DelayQueueMessage entity = param.toEntity();
        // 判断修改预计时间后是否需要加载到队列中
        DelayQueueMeta queueMeta = queueMetaService.getById(queueMessage.getQueueId());
        if (queueMeta == null) {
            throw new CommonException("队列不存在!");
        }
        LocalDateTime nextPreLoadTime = queueMeta.getLastLoadTime().plus(queueMeta.getPreloadDistance(), queueMeta.getTimeUnit().getChronoUnit());
        if (entity.getExpectTime().isBefore(nextPreLoadTime)) {
            entity.setStatus(DelayQueueMessageStatus.loaded);
            // 消息先入库
            this.updateById(entity);
            DelayQueueUtils.pushQueue(queueMeta.getName(), entity);
            return;
        }
        this.updateById(entity);
    }

    @Override
    public void delete(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        this.removeByIds(ids);
    }

    @Override
    public void importExcel(Long queueId, InputStream in) {
        DelayQueueMeta queueMeta = queueMetaService.getById(queueId);
        if (queueMeta == null) {
            throw new CommonException("队列不存在!");
        }
        List<DelayQueueMessageImportParam> paramList = ExcelUtil.readExcel(in, DelayQueueMessageImportParam.class);
        List<DelayQueueMessage> saveList = paramList.stream().map(DelayQueueMessageImportParam::toEntity).toList();
        this.pushMessage(queueMeta, saveList);
    }

    @Override
    public void importExcelTemplate(OutputStream out) {
        ExcelUtil.writeExcel(out, new ExcelSheet<>(Collections.emptyList(), DelayQueueMessageImportParam.class));
    }

    @Override
    public void exportExcel(DelayQueueMessagePageParam param, OutputStream out) {
        List<DelayQueueMessage> dataList = this.list(getQueryWrapper(param));
        List<DelayQueueMessageExportResult> exportList = dataList.stream().map(DelayQueueMessageExportResult::of).toList();
        ExcelUtil.writeExcel(out, new ExcelSheet<>(exportList, DelayQueueMessageExportResult.class));
    }
}
